package runner.SAPSO_VM;


import org.cloudbus.cloudsim.Vm;
import org.cloudbus.cloudsim.power.PowerHost;
import runner.jswarm_pso.Particle;
import runner.utils.Calculator;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class SchedulerParticle extends Particle {

    //起始温度
    private double T;

    //终止温度
    private double T_limit;

    //退火速度
    private double a;

    List<Vm> vmList = new ArrayList<>();
    List<PowerHost> hostList = new ArrayList<>();


    SchedulerParticle(List<Vm> vmList, List<PowerHost> hostList) {
        super(vmList.size());

        this.vmList = vmList;
        this.hostList = hostList;

//        double[] position = new double[vmList.size()];
//        double[] velocity = new double[vmList.size()];
//
//        for (int i = 0; i < vmList.size(); i++) {
//            Random randObj = new Random();
//            position[i] = randObj.nextInt(hostList.size());
//            velocity[i] = Math.random();
//        }

        int k =0, vmsSize = vmList.size();

        double[] x = new double[vmsSize];
        double[] cx = new double[vmsSize];
        double[] chaosList = new double[vmsSize]; // 混沌序列
        double[] xOriginal = new double[vmsSize];  // Tent映射后，将序列中的点映射回原空间
        double[] position = new double[vmsSize];  // 粒子的位置
        double[] positionOp = new double[vmsSize]; // 粒子位置的对立点
        double[] velocity = new double[vmsSize];  // 粒子的速度
        double alpha = 0.5;
        for (int i = 0; i < vmsSize; i++) {
//              [0,hostsId.length)中随机一个整数值作为下标（由于迁移的原因，当前得到的position值可能会大于hostsId.size();后面进行资源消耗时可能会出现越界异常——正在解决。。。）
//               position[i] = HostsId[new Random().nextInt(HostsId.length)];
//          1. 设置x的初始位置
            x[i] = new Random().nextInt(hostList.size());   // x[i]的上下界：0-Hosts.size()-1   生成一个随机的int值，该值介于[0,n)的区间
//          2. 将x映射到[0,1]区间上  （公式中a设置为0.49）
            cx[i] = x[i] / (hostList.size()-1);
//          3. 根据Tent映射得到混沌序列
            if ( cx[i] >= 0 && cx[i] <= alpha )
                chaosList[i] = cx[i] / alpha;
            else if ( cx[i] > alpha && cx[i] <= 1.0 )
                chaosList[i] = (1-cx[i]) / (1-alpha);
//          4. 序列中的点映射回原空间
            xOriginal[i] = chaosList[i] * (hostList.size()-1);

            // 初始化位置
//            position[i] = HostsId[(int) xOriginal[i]];
            int x1 = (int)xOriginal[i];
            if ( x1<0 || x1>(hostList.size()-1) )
                x1 = hostList.size()-1;
            position[i] = (int)xOriginal[i];
            // 初始化速度
            velocity[i] = -0.5 + Math.random();
            k = i;
        }
        setPosition(position);
        setVelocity(velocity);
        T=1000.0;
        a=0.95;
    }

//    @Override
//    public String toString() {
//        String output = "";
//        for (int i = 0; i < Constants.NO_OF_HOSTS; i++) {
//            String tasks = "";
//            int no_of_tasks = 0;
//            for (int j = 0; j < Constants.NO_OF_VMS; j++) {
//                if (i == (int) getPosition()[j]) {
//                    tasks += (tasks.isEmpty() ? "" : " ") + j;
//                    ++no_of_tasks;
//                }
//            }
//            if (tasks.isEmpty()) output += "There is no tasks associated to VM " + i + "\n";
//            else
//                output += "There are " + no_of_tasks + " tasks associated to VM " + i + " and they are " + tasks + "\n";
//        }
//        return output;
//    }

    @Override
    public void InitMutation()
    {
        //置换规则获取随机两个任务,并取对应的虚拟机
        double[] position = this.getBestPosition();

        //扰动当前局部最优解,得到first，second的值 置换规则
        Random rd = new Random();
        int first=rd.nextInt(vmList.size());//任务的序号
        int second=rd.nextInt(vmList.size());//另一个任务的序号

        if(position[first]!=position[second])
        {
            //交换虚拟机
            double tmp = 0.0;
            tmp = position[first];
            position[first] = position[second];
            position[second] = tmp;

            //计算新解的适应度值
            double fitness = new SchedulerFitnessFunction(vmList, hostList).evaluate(position);
            if(fitness < this.getBestFitness())
            {
                //接受新解 PSO
                AcceptNewPosition(position, fitness);
            }
            else
            {
                // SA
                CalByMetropolis(position, fitness);
            }
        }
    }

    private void AcceptNewPosition(double[] position, double fitness) {
        setBestPosition(position);
        setBestFitness(fitness);
    }

    private void CalByMetropolis2(double[] position, double fitness) {
        int iter = SAPSO.iter;
        if (iter == 1) {
            T = fitness/Math.log(0.2);
        } else {
            T = T * 0.95;
        }
    }


    private void CalByMetropolis(double[] position, double fitness) {
        //如果交换后任务的适应度值(任务执行时间)较小减少,那么就接受新解,否则根据模拟退火的 Metropolis 准则判断是否接受新解
//        double temp = this.getFitness()-this.getBestFitness();
        int iter = SAPSO.iter;
        if (iter == 1) {
            T = fitness/Math.log(0.2);
        } else {
            T = T * 0.95;
        }
        double temp = fitness - this.getBestFitness();
        double p= temp<=0? 1.0 : Math.exp(-Calculator.div(temp, T));
        if(p==1.0)
        {
            AcceptNewPosition(position, fitness);
        }
        else
        {
            double value = GenerateValueUnderCurrentT();
            if(p>=value)
            {
                AcceptNewPosition(position, fitness);
            }
        }
    }


    private double GenerateValueUnderCurrentT()
    {
        Random rd = new Random();
        double value = rd.nextDouble();
        return value;
    }
}
